package com.zhongen.wflow.workflow.service.impl;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhongen.wflow.bean.entity.WflowModelHistorys;
import com.zhongen.wflow.exception.BusinessException;
import com.zhongen.wflow.mapper.WflowModelHistorysMapper;
import com.zhongen.wflow.mapper.WflowUsersMapper;
import com.zhongen.wflow.service.OrgRepositoryService;
import com.zhongen.wflow.utils.UserUtil;
import com.zhongen.wflow.workflow.bean.dto.NotifyDto;
import com.zhongen.wflow.workflow.bean.dto.ProcessInstanceOwnerDto;
import com.zhongen.wflow.workflow.bean.process.OrgUser;
import com.zhongen.wflow.workflow.bean.process.props.ApprovalProps;
import com.zhongen.wflow.workflow.bean.process.props.CcProps;
import com.zhongen.wflow.workflow.bean.vo.*;
import com.zhongen.wflow.workflow.config.WflowGlobalVarDef;
import com.zhongen.wflow.workflow.extension.cmd.RecallToHisApprovalNodeCmd;
import com.zhongen.wflow.workflow.service.*;
import com.zhongen.wflow.workflow.service.FormService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.ExecutionGraphUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.history.NativeHistoricTaskInstanceQuery;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : willian fu
 * @date : 2022/8/25
 */
@Slf4j
@Service("processTaskService")
public class ProcessTaskServiceImpl implements ProcessTaskService {

	@Autowired
	private TaskService taskService;

	@Autowired
	private RuntimeService runtimeService;

	@Autowired
	private NotifyService notifyService;

	@Autowired
	private FormService formService;

	@Autowired
	private UserDeptOrLeaderService userDeptOrLeaderService;

	@Autowired
	private OrgRepositoryService orgRepositoryService;

	@Autowired
	private HistoryService historyService;

	@Autowired
	private RepositoryService repositoryService;

	@Autowired
	private ManagementService managementService;

	@Autowired
	private WflowModelHistorysMapper historysMapper;

	// 超时缓存，数据缓存20秒，用来存储审批人防止flowable高频调用
	private static final TimedCache<String, List<String>> taskCache = CacheUtil.newTimedCache(20000);

	static {
		taskCache.schedulePrune(10000);
	}

	@Override
	public Page<ProcessTaskVo> getUserTodoList(Integer pageSize, Integer pageNo, String code) {
		String userId = UserUtil.getLoginUserId();
		TaskQuery taskQuery = taskService.createTaskQuery();
		if (StrUtil.isNotBlank(code)) {
			taskQuery.processDefinitionKey(code);
		}
		taskQuery.active().taskCandidateOrAssigned(userId).orderByTaskCreateTime().desc();
		Page<ProcessTaskVo> page = new Page<>();
		List<Task> taskList = taskQuery.listPage(pageSize * (pageNo - 1), pageSize);
		page.setTotal(taskQuery.count());
		page.setCurrent(pageNo);
		page.setSize(pageSize);
		page.setRecords(Collections.emptyList());
		Set<String> staterUsers = new HashSet<>();
		List<ProcessTaskVo> taskVos = taskList.stream().map(task -> {
			ProcessDefinition processDef = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(task.getProcessDefinitionId())
				.singleResult();
			// 从缓存取
			ProcessInstanceOwnerDto owner = runtimeService.getVariable(task.getProcessInstanceId(), "owner",
					ProcessInstanceOwnerDto.class);
			if (Objects.isNull(owner)) {
				owner = ProcessInstanceOwnerDto.builder()
					.owner("")
					.ownerDeptName("未知")
					.ownerDeptId("")
					.ownerName("未知")
					.build();
			}
			ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
				.processInstanceId(task.getProcessInstanceId())
				.singleResult();
			staterUsers.add(owner.getOwner());
			return ProcessTaskVo.builder()
				.taskId(task.getId())
				.taskName(task.getName())
				.taskDefKey(task.getTaskDefinitionKey())
				.processDefId(task.getProcessDefinitionId())
				.executionId(task.getExecutionId())
				.nodeId(task.getTaskDefinitionKey())
				.deployId(processDef.getDeploymentId())
				.processDefName(processDef.getName())
				.version(processDef.getVersion())
				.instanceId(task.getProcessInstanceId())
				.superInstanceId(processInstance.getRootProcessInstanceId())
				.ownerId(processInstance.getStartUserId())
				.ownerDeptId(owner.getOwnerDeptId())
				.ownerDeptName(owner.getOwnerDeptName())
				.createTime(processInstance.getStartTime())
				.taskCreateTime(task.getCreateTime())
				.build();
		}).collect(Collectors.toList());
		// 取用户信息，减少数据库查询，一次构建
		if (CollectionUtil.isNotEmpty(staterUsers)) {
			Map<String, OrgUser> userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers);
			page.setRecords(
					taskVos.stream().peek(v -> v.setOwner(userMap.get(v.getOwnerId()))).collect(Collectors.toList()));
		}
		return page;
	}

	@Override
	public Page<ProcessTaskVo> getUserIdoList(Integer pageSize, Integer pageNo, String code) {
		String userId = UserUtil.getLoginUserId();
		// 自定义sql查询所有已办的任务
		String nativeSql = StrUtil.builder("SELECT aht.* FROM `ACT_HI_TASKINST` AS aht ")
			.append("LEFT JOIN `ACT_HI_VARINST` AS ahv ON SUBSTRING(ahv.NAME_, 9) = aht.ID_ AND ahv.NAME_ LIKE 'approve_%'")
			.append("LEFT JOIN `ACT_RE_PROCDEF` AS arp ON arp.ID_ = aht.PROC_DEF_ID_ ")
			.append("WHERE aht.ASSIGNEE_ = '")
			.append(userId)
			.append("' ")
			.append(StrUtil.isNotBlank(code) ? (" AND arp.KEY_ = '" + code + "' ") : " ")
			.append("AND ahv.NAME_ IS NOT NULL ORDER BY aht.END_TIME_ DESC")
			.toString();
		NativeHistoricTaskInstanceQuery countQuery = historyService.createNativeHistoricTaskInstanceQuery()
			.sql("SELECT COUNT(0) FROM (" + nativeSql + ") AS ct");
		NativeHistoricTaskInstanceQuery taskInstanceQuery = historyService.createNativeHistoricTaskInstanceQuery()
			.sql(nativeSql);
		List<HistoricTaskInstance> taskInstances = taskInstanceQuery.listPage(pageSize * (pageNo - 1), pageSize);
		// 把已办任务流程实例一次性取出来，减少查询次数
		Map<String, HistoricProcessInstance> instanceMap = CollectionUtil.isNotEmpty(taskInstances) ? historyService
			.createHistoricProcessInstanceQuery()
			.processInstanceIds(
					taskInstances.stream().map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet()))
			.list()
			.stream()
			.collect(Collectors.toMap(HistoricProcessInstance::getId, v -> v)) : new HashMap<>();
		Page<ProcessTaskVo> page = new Page<>();
		page.setTotal(countQuery.count());
		page.setCurrent(pageNo);
		page.setSize(pageSize);
		Set<String> staterUsers = new HashSet<>();
		List<ProcessTaskVo> taskVos = taskInstances.stream().map(task -> {
			HistoricProcessInstance instance = instanceMap.get(task.getProcessInstanceId());
			HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery()
				.processInstanceId(instance.getId())
				.variableName("approve_" + task.getId())
				.singleResult();
			// 从缓存取
			staterUsers.add(instance.getStartUserId());
			return ProcessTaskVo.builder()
				.taskId(task.getId())
				.taskName(task.getName())
				.taskDefKey(task.getTaskDefinitionKey())
				.processDefId(task.getProcessDefinitionId())
				.executionId(task.getExecutionId())
				.nodeId(task.getTaskDefinitionKey())
				.deployId(instance.getDeploymentId())
				.superInstanceId(Optional.ofNullable(instance.getSuperProcessInstanceId()).orElse(instance.getId()))
				.processDefName(instance.getProcessDefinitionName())
				.version(instance.getProcessDefinitionVersion())
				.instanceId(task.getProcessInstanceId())
				.ownerId(instance.getStartUserId())
				.createTime(instance.getStartTime())
				.taskCreateTime(task.getCreateTime())
				.taskEndTime(task.getEndTime())
				.taskResult(String.valueOf(Objects.nonNull(variableInstance) ? variableInstance.getValue()
						: ProcessHandlerParamsVo.Action.recall))
				.build();
		}).collect(Collectors.toList());
		// 取用户信息，减少数据库查询，一次构建
		if (CollectionUtil.isNotEmpty(staterUsers)) {
			Map<String, OrgUser> userMap = userDeptOrLeaderService.getUserMapByIds(staterUsers);
			page.setRecords(
					taskVos.stream().peek(v -> v.setOwner(userMap.get(v.getOwnerId()))).collect(Collectors.toList()));
		}
		return page;
	}

	@Override
	@Transactional
	public void approvalTask(ProcessHandlerParamsVo params) {
		String userId = UserUtil.getLoginUserId();
		Authentication.setAuthenticatedUserId(userId);
		Task task = null;
		if (StrUtil.isNotBlank(params.getTaskId())) {
			task = taskService.createTaskQuery().taskId(params.getTaskId()).active().singleResult();
		}
		if (hasComment(params.getComment())) {
			boolean putComment = Objects.nonNull(task) && !userId.equals(task.getAssignee());
			boolean isComment = ProcessHandlerParamsVo.Action.comment.equals(params.getAction());
			if (ProcessHandlerParamsVo.Action.cancel.equals(params.getAction())) {
				params.getComment().setText("撤销：" + params.getComment().getText());
			}
			taskService.addComment(isComment || putComment ? null : params.getTaskId(), params.getInstanceId(),
					JSONObject.toJSONString(params.getComment()));
		}
		if (StrUtil.isNotBlank(params.getSignature())) {
			if (params.getUpdateSign()) {
				orgRepositoryService.updateUserSign(userId, params.getSignature());
			}
		}

		if (!ProcessHandlerParamsVo.Action.comment.equals(params.getAction())) {
			if (ObjectUtil.isNull(task) && !ProcessHandlerParamsVo.Action.cancel.equals(params.getAction())) {
				throw new BusinessException("任务不存在");
			}
			else if (ObjectUtil.isNotNull(task) && "root".equals(task.getTaskDefinitionKey())
					&& !(ProcessHandlerParamsVo.Action.agree.equals(params.getAction())
							|| ProcessHandlerParamsVo.Action.cancel.equals(params.getAction()))) {
				throw new BusinessException("发起人节点不支持此项操作");
			}
			switch (params.getAction()) {
				case agree:
				case refuse:
					formService.updateInstanceFormData(params.getInstanceId(), params.getFormData());
					doApproval(task, params);
					break;
				case recall: // 退回
					doRecallTask(task, userId, params);
					break;
				case transfer: // 转交
					formService.updateInstanceFormData(params.getInstanceId(), params.getFormData());
					doTransferTask(task, params, userId);
					break;
				case afterAdd:
				case beforeAdd: // 加签，暂时只支持后加签
					// 加签如果是顺序会签，暂时先禁止，
					BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
					UserTask userTask = (UserTask) bpmnModel.getMainProcess()
						.getFlowElement(task.getTaskDefinitionKey());
					MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();
					if (Objects.isNull(loopCharacteristics) || loopCharacteristics.isSequential()) {
						throw new BusinessException("当前节点不支持临时加签");
					}
					runtimeService.addMultiInstanceExecution(task.getTaskDefinitionKey(), task.getProcessInstanceId(),
							Collections.singletonMap("assignee", params.getTargetUser()));
					log.info("[{}]将任务[{}]加签给[{}]处理", userId, task.getId(), params.getTargetUser());
					break;
				case cancel:
					List<ActivityInstance> instances = runtimeService.createActivityInstanceQuery()
						.processInstanceId(params.getInstanceId())
						.unfinished()
						.list();
					doCancelProcess(params.getInstanceId(),
							instances.stream()
								.filter(v -> v.getActivityType().equalsIgnoreCase("userTask"))
								.findFirst()
								.get()
								.getActivityId());
					break;
				case comment:
					// throw new BusinessException("暂不支持单独评论");
					break;
			}
		}
		Authentication.setAuthenticatedUserId(null);
	}

	@Override
	public Set<String> getCcTaskUsers(DelegateExecution execution, String instance, String nodeId) {
		// 获取设置项
		HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery()
			.processInstanceId(instance)
			.variableName(WflowGlobalVarDef.WFLOW_NODE_PROPS)
			.singleResult();
		Map nodeProps;
		if (Objects.nonNull(variableInstance)) {
			nodeProps = (Map) variableInstance.getValue();
		}
		else {
			// 流程首个节点需要从执行实例中取数据
			nodeProps = execution.getVariable(WflowGlobalVarDef.WFLOW_NODE_PROPS, Map.class);
		}
		CcProps ccProps = (CcProps) nodeProps.get(nodeId);
		// 获取变量里面自选的抄送人
		Set<String> ccUsers = new HashSet<>();
		List<OrgUser> orgs = new ArrayList<>(ccProps.getAssignedUser());
		if (ccProps.getShouldAdd()) {
			// 获取发起流程时添加的抄送人
			HistoricVariableInstance result = historyService.createHistoricVariableInstanceQuery()
				.processInstanceId(instance)
				.variableName(nodeId)
				.singleResult();
			if (Objects.nonNull(result)) {
				Optional.ofNullable(result.getValue()).ifPresent(us -> orgs.addAll((List<OrgUser>) us));
			}
			else {
				Optional.ofNullable(execution.getVariable(nodeId, List.class)).ifPresent(orgs::addAll);
			}
		}
		// 解析部门与人员选项
		ccUsers.addAll(orgs.stream()
			.filter(org -> "user".equals(org.getType()))
			.map(OrgUser::getId)
			.collect(Collectors.toSet()));
		ccUsers.addAll(userDeptOrLeaderService.getUsersByDept(orgs.stream()
			.filter(org -> "dept".equals(org.getType()))
			.map(OrgUser::getId)
			.collect(Collectors.toSet())));
		return ccUsers;
	}

	@Override
	public List<String> getNodeApprovalUsers(ExecutionEntity execution) {
		// 取缓存里面的，判断之前有没有，多实例防止多次解析
		List<String> cacheUsers = taskCache.get(execution.getProcessInstanceId() + execution.getActivityId());
		if (Objects.nonNull(cacheUsers)) {
			return cacheUsers;
		}
		log.info("获取节点[{}]的审批人", execution.getActivityId());
		Map propsMap = execution.getVariable(WflowGlobalVarDef.WFLOW_NODE_PROPS, Map.class);
		ApprovalProps props = (ApprovalProps) propsMap.get(execution.getActivityId());
		List<String> approvalUsers = getApprovalUsers(execution, props);
		taskCache.put(execution.getProcessInstanceId() + execution.getActivityId(), approvalUsers);
		return approvalUsers;
	}

	@Override
	public List<ProcessProgressVo.ProgressNode> getFutureTask(String instanceId) {
		// 根据流程遍历后续节点，期间要穿越后续包含并行网关和条件网关的节点
		return Collections.emptyList();
	}

	@Override
	public List<HisApprovalNodeVo> getRecallTaskNodes(String instanceId, String taskId) {
		ProcessInstance instance = runtimeService.createProcessInstanceQuery()
			.processInstanceId(instanceId)
			.singleResult();
		// 获取当前用户的所有待审批任务
		String userId = UserUtil.getLoginUserId();
		Task task = taskService.createTaskQuery()
			.processInstanceId(instanceId)
			.taskCandidateOrAssigned(userId)
			.taskId(taskId)
			.active()
			.singleResult();
		if (Objects.isNull(task)) {
			throw new BusinessException("该任务不存在");
		}
		// 获取已经处理过的任务
		List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery()
			.processInstanceId(instanceId)
			.finished()
			.orderByActivityInstanceStartTime()
			.asc()
			.list();
		Map<String, String> collect = activityInstances.stream()
			.filter(act -> BpmnXMLConstants.ELEMENT_TASK_USER.equals(act.getActivityType()))
			.collect(Collectors.toMap(ActivityInstance::getActivityId, ActivityInstance::getActivityName,
					(v1, v2) -> v2));
		// 移除当前的自身节点
		collect.remove(task.getTaskDefinitionKey());
		if (CollectionUtil.isNotEmpty(collect)) {
			BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());
			// 获取当前节点
			FlowNode nowNode = (FlowNode) bpmnModel.getFlowElement(task.getTaskDefinitionKey());
			Set<String> hisNodes = new HashSet<>();
			loadBeforeSerialUserTaskNode(nowNode, hisNodes, 0);
			// 取交集
			Collection<String> intersection = new HashSet<>(CollectionUtil.intersection(collect.keySet(), hisNodes));
			// 获取任务所在审批节点
			return collect.keySet().stream().filter(key -> {
				FlowNode sflowNode = (FlowNode) bpmnModel.getFlowElement(key);
				FlowNode tflowNode = (FlowNode) bpmnModel.getFlowElement(task.getTaskDefinitionKey());
				return ExecutionGraphUtil.isReachable(bpmnModel.getMainProcess(), sflowNode, tflowNode, new HashSet<>())
						&& intersection.contains(key);
			}).map(key -> new HisApprovalNodeVo(key, collect.getOrDefault(key, "--"))).collect(Collectors.toList());
		}
		return Collections.emptyList();
	}

	@Override
	public NodeSettingsVo getNodeTaskSettings(String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (Objects.isNull(task)) {
			throw new BusinessException("该任务已被处理，请刷新数据");
		}
		WflowModelHistorys modelHistory = historysMapper.selectOne(new LambdaQueryWrapper<WflowModelHistorys>()
			.eq(WflowModelHistorys::getProcessDefId, task.getProcessDefinitionId()));
		if (Objects.nonNull(modelHistory)) { // 子流程的话，这里是null
			JSONObject object = JSONObject.parseObject(modelHistory.getSettings());
			Boolean sign = object.getBoolean("sign");
			// 先查全局设置，如果没开启签名，就再查流程节点设置
			if (Boolean.TRUE.equals(sign)) {
				return NodeSettingsVo.builder().enableSign(true).build();
			}
		}
		Map variable = (Map) runtimeService.getVariable(task.getProcessInstanceId(),
				WflowGlobalVarDef.WFLOW_NODE_PROPS);
		Object nodeProps = variable.get(task.getTaskDefinitionKey());
		return NodeSettingsVo.builder()
			.enableSign(nodeProps instanceof ApprovalProps && ((ApprovalProps) nodeProps).isSign())
			.build();
	}

	/**
	 * 获取审批人
	 * @param execution 上下文
	 * @param props 节点熟悉
	 * @return 审批人ID列表
	 */
	public List<String> getApprovalUsers(ExecutionEntity execution, ApprovalProps props) {
		Set<String> userSet = new LinkedHashSet<>();
		switch (props.getAssignedType()) {
			case REFUSE:
				userSet.add(WflowGlobalVarDef.WFLOW_TASK_REFUSE);
				break;
			case SELF: // 取流程发起人
				ProcessInstanceOwnerDto owner = execution.getVariable("owner", ProcessInstanceOwnerDto.class);
				Optional.ofNullable(owner).ifPresent(on -> userSet.add(on.getOwner()));
				break;
			case ROLE: // 取角色
				userSet.addAll(userDeptOrLeaderService
					.getUsersByRoles(props.getRole().stream().map(OrgUser::getId).collect(Collectors.toList())));
				break;
			case FORM_USER: // 从表单取
				List<Map<String, Object>> userList = execution.getVariable(props.getFormUser(), List.class);
				Optional.ofNullable(userList).ifPresent(users -> {
					userSet.addAll(users.stream().map(u -> u.get("id").toString()).collect(Collectors.toList()));
				});
				break;
			case FORM_DEPT: // 从表单取
				List<Map<String, Object>> deptList = execution.getVariable(props.getFormDept(), List.class);
				Optional.ofNullable(deptList).ifPresent(users -> {
					userSet.addAll(userDeptOrLeaderService.getLeadersByDept(
							users.stream().map(u -> u.get("id").toString()).collect(Collectors.toList())));
				});
				break;
			case ASSIGN_USER:// 指定用户
				userSet.addAll(props.getAssignedUser().stream().map(OrgUser::getId).collect(Collectors.toList()));
				break;
			case ASSIGN_LEADER:
				List<String> collect = props.getAssignedDept()
					.stream()
					.map(OrgUser::getId)
					.collect(Collectors.toList());
				userSet.addAll(userDeptOrLeaderService.getLeadersByDept(collect));
				break;
			case SELF_SELECT: // 自选用户，从变量取，这一步在发起流程时设置的
				List<OrgUser> selectUsers = execution.getVariable(execution.getActivityId(), List.class);
				Optional.ofNullable(selectUsers)
					.ifPresent(on -> userSet.addAll(on.stream().map(OrgUser::getId).collect(Collectors.toList())));
				break;
			case LEADER: // 用户的指定级别部门主管
				ProcessInstanceOwnerDto owner2 = execution.getVariable("owner", ProcessInstanceOwnerDto.class);
				String leaderByLevel = userDeptOrLeaderService.getUserLeaderByLevel(owner2.getOwner(),
						owner2.getOwnerDeptId(), props.getLeader().getLevel(), props.getLeader().getSkipEmpty());
				Optional.ofNullable(leaderByLevel).ifPresent(userSet::add);
				break;
			case LEADER_TOP: // 用户逐级部门主管
				ProcessInstanceOwnerDto owner3 = execution.getVariable("owner", ProcessInstanceOwnerDto.class);
				List<String> leaders = userDeptOrLeaderService.getUserLeadersByLevel(owner3.getOwner(),
						owner3.getOwnerDeptId(),
						"TOP".equals(props.getLeaderTop().getEndCondition()) ? 0 : props.getLeaderTop().getEndLevel(),
						props.getLeaderTop().getSkipEmpty());
				Optional.ofNullable(leaders).ifPresent(userSet::addAll);
				break;
		}
		// 处理审批人为空时，采取默认策略
		if (CollectionUtil.isEmpty(userSet)) {
			switch (props.getNobody().getHandler()) {
				case TO_USER:
					userSet.addAll(props.getNobody()
						.getAssignedUser()
						.stream()
						.map(OrgUser::getId)
						.collect(Collectors.toList()));
					break;
				case TO_ADMIN: // TODO 注意系统需要包含该角色 WFLOW_APPROVAL_ADMIN
					userSet.addAll(userDeptOrLeaderService
						.getUsersByRoles(CollectionUtil.newArrayList(WflowGlobalVarDef.WFLOW_APPROVAL_ADMIN)));
					break;
				case TO_PASS:
					userSet.add(WflowGlobalVarDef.WFLOW_TASK_AGRRE);
					break;
				case TO_REFUSE:
					userSet.add(WflowGlobalVarDef.WFLOW_TASK_REFUSE);
					break;
			}
		}
		else {
			// 将用户替换为当前代理人
			return userDeptOrLeaderService.replaceUserAsAgent(userSet);
		}
		return new ArrayList<>(userSet);
	}

	private boolean hasComment(ProcessHandlerParamsVo.ProcessComment comment) {
		return Objects.nonNull(comment)
				&& (StrUtil.isNotBlank(comment.getText()) || CollectionUtil.isNotEmpty(comment.getAttachments()));
	}

	/**
	 * 审批任务，同意、驳回
	 * @param task 当前任务
	 * @param params 参数
	 */
	private void doApproval(Task task, ProcessHandlerParamsVo params) {
		HashMap<String, Object> var = MapUtil.newHashMap();
		// var.put("approve",
		// params.getAction().equals(ProcessHandlerParamsVo.Action.agree));
		var.put("approve_" + task.getId(), params.getAction());
		if (StrUtil.isNotBlank(params.getSignature())) {
			var.put("sign_" + task.getId(), params.getSignature());
		}
		taskService.complete(params.getTaskId(), var);
	}

	private void doRecallTask(Task task, String userId, ProcessHandlerParamsVo params) {
		// 执行自定义回退逻辑
		HashMap<String, Object> var = MapUtil.newHashMap();
		var.put("approve_" + params.getTaskId(), params.getAction());
		managementService
			.executeCommand(new RecallToHisApprovalNodeCmd(runtimeService, params.getTaskId(), params.getTargetNode()));
		runtimeService.setVariables(params.getInstanceId(), var);
		if ("root".equals(params.getTargetNode())) {
			// 如果是退回发起人节点，那么推送一条通知消息，因为发起人节点没有设置任务监听器，所以不会触发UserTaskListener
			ProcessInstance instance = runtimeService.createProcessInstanceQuery()
				.processInstanceId(task.getProcessInstanceId())
				.singleResult();
			notifyService.notify(NotifyDto.builder()
				.title("您的审批被退回")
				.instanceId(task.getProcessInstanceId())
				.target(instance.getStartUserId())
				.processDefId(task.getProcessDefinitionId())
				.content(StrUtil.builder("您提交的【", instance.getProcessDefinitionName(), "】被退回，请即时处理").toString())
				.type(NotifyDto.TypeEnum.WARNING)
				.build());
		}
		log.info("用户[{}] 退回流程[{}] [{} -> {}]", userId, params.getInstanceId(), task.getTaskDefinitionKey(),
				params.getTargetNode());
	}

	/**
	 * 转交任务处理
	 * @param task 当前任务
	 * @param params 参数
	 * @param userId 任务当前处理人
	 */
	private void doTransferTask(Task task, ProcessHandlerParamsVo params, String userId) {
		taskService.setOwner(params.getTaskId(), userId);
		taskService.setAssignee(params.getTaskId(), params.getTargetUser());
		OrgUser orgUser = userDeptOrLeaderService.getUserMapByIds(CollectionUtil.newArrayList(userId)).get(userId);
		notifyService.notify(NotifyDto.builder()
			.title("待处理的转交任务")
			.instanceId(task.getProcessInstanceId())
			.target(params.getTargetUser())
			.nodeId(task.getTaskDefinitionKey())
			.processDefId(task.getProcessDefinitionId())
			.content(StrUtil.builder(orgUser.getName(), "转交了一项任务给您处理").toString())
			.type(NotifyDto.TypeEnum.WARNING)
			.build());
		log.info("[{}]将任务[{}]转交给[{}]处理", userId, task.getId(), params.getTargetUser());
	}

	/**
	 * 取消流程处理
	 * @param instanceId 流程实例ID
	 * @param nodeId 当前流程所处节点ID
	 */
	private void doCancelProcess(String instanceId, String nodeId) {
		List<Execution> executions = runtimeService.createExecutionQuery()
			.parentId(instanceId)
			.onlyChildExecutions()
			.list();
		// 强制流程指向驳回
		runtimeService.createChangeActivityStateBuilder()
			.processInstanceId(instanceId)
			.moveActivityIdTo(nodeId, "cancel-end")
			.moveExecutionsToSingleActivityId(executions.stream().map(Execution::getId).collect(Collectors.toList()),
					"cancel-end")
			.changeState();
	}

	/**
	 * 向上遍历加载用户任务审批节点
	 * @param nowNode 当前节点
	 * @param list 审批节点集合
	 */
	private void loadBeforeSerialUserTaskNode(FlowNode nowNode, Set<String> list, Integer index) {
		// 按串行向上遍历
		List<SequenceFlow> incomingFlows = nowNode.getIncomingFlows();
		FlowElement beforeNode = incomingFlows.get(index).getSourceFlowElement();
		if (beforeNode instanceof UserTask) {
			list.add(beforeNode.getId());
			loadBeforeSerialUserTaskNode((FlowNode) beforeNode, list, 0);
		}
		else if (beforeNode instanceof ParallelGateway || beforeNode instanceof InclusiveGateway) {
			// 碰到并行和包容网关，判断是不是合流点，是就绕过继续往上，不是就截止
			if (((Gateway) beforeNode).getOutgoingFlows().size() == 1) {
				// 只有一条出线，那么就是合流点
				SequenceFlow sequenceFlow = ((Gateway) beforeNode).getIncomingFlows().get(0);
				FlowElement gateway = getStartGateway(sequenceFlow.getSourceFlowElement(),
						beforeNode instanceof ParallelGateway);
				if (Objects.nonNull(gateway)) {
					loadBeforeSerialUserTaskNode((FlowNode) gateway, list, 0);
				}
			}
		}
		else if (beforeNode instanceof ExclusiveGateway) {
			// 碰到排他网关，里面每一条分支都要遍历
			List<SequenceFlow> flows = ((ExclusiveGateway) beforeNode).getIncomingFlows();
			for (int i = 0; i < flows.size(); i++) {
				loadBeforeSerialUserTaskNode((FlowNode) beforeNode, list, i);
			}
		}
	}

	/**
	 * 获取开始的并行/包容网关节点
	 * @param node 节点
	 * @param isPar 是否为并行网关
	 * @return 开始的网关
	 */
	private FlowElement getStartGateway(FlowElement node, Boolean isPar) {
		if ((isPar && node instanceof ParallelGateway) || (!isPar && node instanceof InclusiveGateway)) {
			// 找到了对应的网关起始节点
			return node;
		}
		FlowNode flowNode = (FlowNode) node;
		return getStartGateway(flowNode.getIncomingFlows().get(0).getSourceFlowElement(), isPar);
	}

}
